package xnet

import (
	"gitee.com/shuohe/xutils/xlog"
	"net"
	"time"
)

//=============================================================================

type TCPChannelFactory func(*net.TCPConn, *TCPServer, int) (*TCPChannel, error)

type TCPServer struct {
	listener *net.TCPListener

	// 自己的事件处理器
	callback ITCPServerCallback

	// 用于从 net.TCPConn 创建 TCPChannel 对象
	chnlFactory TCPChannelFactory

	// 新接受一条连接时使用的 Id 号
	maxChnlId int

	// 所侦听的地址
	ListenAddr *net.TCPAddr

	// 维护已经建立起来的连接
	Channels map[int]*TCPChannel

	IsServing bool
}

func (this *TCPServer) nextChannelId() int {
	this.maxChnlId++
	return this.maxChnlId
}

func (this *TCPServer) close() {
	if this.callback != nil {
		this.callback.OnClose()
	}
	if this.listener != nil {
		this.listener.Close()
	}

	// 通知所有已建立的连接关闭（并删除关联）
	for _, ch := range this.Channels {
		ch.Close()
	}
}

func (this *TCPServer) SetCallback(cb ITCPServerCallback) {
	this.callback = cb
	this.callback.SetServer(this)
}

func (this *TCPServer) SetChannelFactory(f TCPChannelFactory) {
	this.chnlFactory = f
}

// 关闭后可以再次调用 Serve() 启用。
func (this *TCPServer) Close() {
	if this.IsServing {
		// 设置关闭标志，让 Serve() 函数跳出循环
		this.IsServing = false
	} else {
		this.close()
	}
}

func (this *TCPServer) Serve() {
	if this.IsServing || this.callback == nil || this.chnlFactory == nil {
		return
	}

	// 建立侦听
	l, err := net.ListenTCP("tcp4", this.ListenAddr)
	if err != nil {
		xlog.Errorfe(err, "Failed to listen.")
		return
	}
	this.listener = l
	this.callback.OnListen()

	this.IsServing = true

	// 无限循环
	for this.IsServing {
		// 接受连接
		this.listener.SetDeadline(time.Now().Add(time.Second))
		conn, err := this.listener.AcceptTCP()
		if err != nil {
			e, ok := err.(net.Error)
			if ok && e.Timeout() {
				// 已经阻塞了一秒钟
				continue
			} else {
				xlog.Errorfe(err, "Failed to accept connection.")
				continue
			}
		}

		ch, err := this.chnlFactory(conn, this, this.nextChannelId())
		if err != nil {
			xlog.Errorfe(err, "Failed to create channel object.")
			conn.Close()
			continue
		}
		// 将连接加入连接表
		// 在连接关闭时，会自动从连接表中删除
		this.Channels[ch.Id] = ch

		// TCPServer 回调
		err = this.callback.OnAccept(ch)
		if err != nil {
			xlog.Errorfe(err, "OnAccept callback failed.")
			ch.Close()
			continue
		}

		// TCPChannel 回调
		// OnConnect() 负责进行连接的握手逻辑，并在必要时启动连接的事件处理
		go func() {
			err = ch.callback.OnConnect()
			if err != nil {
				xlog.Errorfe(err, "OnConnect callback failed.")
				ch.Close()
			}
		}()
	}

	this.close()
}

func NewTCPServer(listenAddr *net.TCPAddr, callback ITCPServerCallback, chnlFactory TCPChannelFactory) *TCPServer {
	svr := &TCPServer{
		ListenAddr: listenAddr,
		Channels:   make(map[int]*TCPChannel),
	}
	svr.SetCallback(callback)
	svr.SetChannelFactory(chnlFactory)

	return svr
}
